package com.globefrog.framework.log.proxy;

import com.globefrog.framework.common.constant.LogConstants;
import com.globefrog.framework.log.ActionLog;
import com.globefrog.framework.log.ActionLogImpl;
import com.globefrog.framework.log.util.StringExtUtil;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

/**
 * 日志代理callable,多线程支持日志追踪.
 *
 * @author caijinbang
 * @date 2020-11-08 15:35
 */
@Slf4j
public class LogProxyCallable<T> implements Callable<T> {

  /**
   * 代理线程.
   */
  protected Callable<T> delegate;

  /**
   * 父线程id.
   */
  private long parenteThreadId;

  /**
   * 父线程请求id.
   */
  private String parentRequestId;

  /**
   * 父线程动作.
   */
  private String parentAction;

  /**
   * 父业务上下文.
   */
  private final String parentBusinessContext;


  public LogProxyCallable(Callable<T> delegate) {
    // 代理线程
    this.delegate = delegate;
    // 父线程id
    this.parenteThreadId = Thread.currentThread().getId();
    // 获取请求id
    String requestId = MDC.get(LogConstants.MDC_REQUEST_ID);
    // 获取当前线程的actionLog
    ActionLog actionLog = ActionLogImpl.getInstance().currentActionLog();
    // 如果当前MDC中无父线程请求id,那么则从当前动作日志中获取请求id
    String actionRequestId = actionLog == null ? "" : actionLog.getRequestId();
    this.parentRequestId = StringExtUtil.isNotEmpty(requestId) ? requestId : actionRequestId;
    String action = MDC.get(LogConstants.MDC_ACTION);
    String actionName = actionLog == null ? "" : actionLog.getAction();
    this.parentAction = StringExtUtil.isNotEmpty(action) ? action : actionName;
    String businessContext = MDC.get(LogConstants.MDC_BUSINESS_CONTEXT);
    this.parentBusinessContext = !StringUtils.isEmpty(businessContext) ? businessContext : "";
  }


  /**
   * 执行call初始化信息.
   *
   * @return 对象
   * @throws Exception 异常
   */
  @Override
  public T call() throws Exception {
    // 初始化MDC
    this.initLogMdc();
    T finalResult;
    try {
      log.debug("start new thread. currentThread={}", Thread.currentThread().getId());
      // 执行子线程的callable的call方法
      T result = delegate.call();
      log.debug("end new thread. currentThread={}", Thread.currentThread().getId());
      finalResult = result;
    } finally {
      //线程最后执行MDC清除
      MDC.clear();
    }
    return finalResult;
  }

  /**
   * 初始化线程MDC数据.
   */
  protected void initLogMdc() {
    // 先清理MDC内容再初始化线程数据
    MDC.clear();
    MDC.put(LogConstants.MDC_TARGET_THREAD_ID, String.valueOf(parenteThreadId));
    MDC.put(LogConstants.MDC_REQUEST_ID, parentRequestId);
    MDC.put(LogConstants.MDC_ACTION, parentAction);
    MDC.put(LogConstants.MDC_BUSINESS_CONTEXT, parentBusinessContext);
  }

  public String getParentRequestId() {
    return parentRequestId;
  }

  public String getParentAction() {
    return parentAction;
  }

  public String getParentBusinessContext() {
    return parentBusinessContext;
  }
}
